package com.identity.identity.dect;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Rect;
import android.hardware.Camera;
import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;
import android.text.TextUtils;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;

import com.identity.identity.R;
import com.identity.identity.dect.util.ActivityUtils;
import com.identity.identity.dect.view.CameraPreview;
import com.identity.identity.dect.view.OverlayView;
import com.identity.identity.dect.view.SenseCamera;
import com.identity.identity.dect.view.SimpleImageStore;
import com.sensetime.senseid.sdk.liveness.silent.FaceOcclusion;
import com.sensetime.senseid.sdk.liveness.silent.OnLivenessListener;
import com.sensetime.senseid.sdk.liveness.silent.SilentLivenessApi;
import com.sensetime.senseid.sdk.liveness.silent.common.type.PixelFormat;
import com.sensetime.senseid.sdk.liveness.silent.common.type.ResultCode;
import com.sensetime.senseid.sdk.liveness.silent.common.type.Size;
import com.sensetime.senseid.sdk.liveness.silent.common.util.FileUtil;
import com.sensetime.senseid.sdk.liveness.silent.type.FaceDistance;
import com.sensetime.senseid.sdk.liveness.silent.type.FacePosition;
import com.sensetime.senseid.sdk.liveness.silent.type.LightIntensity;
import com.sensetime.senseid.sdk.liveness.silent.type.OcclusionStatus;

import java.io.File;
import java.util.List;

public class IdentityActivity extends AppCompatActivity implements Camera.PreviewCallback, CameraPreview.StartListener {
    public static final String RESULT_DEAL_ERROR_INNER = "result_deal_error_inner";
    public static final String RESULT_SDK_ERROR_CODE = "result_sdk_error_code";
    public static final String RESULT_IMAGE_KEY = "result_image_key";
    public static final String RESULT_FACE_RECT = "result_face_rect";
    public static final String RESULT_INFO = "result_info";
    protected static final String LICENSE_FILE_NAME = "Haolinmeng_SenenID_Silent.lic";
    protected static final String DETECTION_MODEL_FILE_NAME = "M_Detect_Hunter_SmallFace.model";
    protected static final String ALIGNMENT_MODEL_FILE_NAME = "M_Align_occlusion.model";
    protected static final String QUALITY_MODEL_FILE_NAME = "M_Face_Quality.model";
    protected static final String FRAME_SELECTOR_MODEL_FILE_NAME = "M_Liveness_Cnn_half.model";
    protected static final String ANTI_SPOOFING_MODEL_FILE_NAME = "M_Liveness_Antispoofing.model";
    protected TextView mTipsView = null;
    protected CameraPreview mCameraPreviewView = null;
    protected SenseCamera mSenseCamera;

    protected boolean mStartInputData = false;
    protected boolean mIsCanceled = true;
    public OverlayView overlay;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Window window = getWindow();
            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.setStatusBarColor(Color.TRANSPARENT);
        }

        if (!checkPermission(Manifest.permission.CAMERA,
                Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
            final Intent data = new Intent();
            data.putExtra(RESULT_DEAL_ERROR_INNER, true);
            showError("权限检测失败，请检查应用权限设置");
            setResult(ActivityUtils.RESULT_CODE_NO_PERMISSIONS, data);
            finish();
            return;
        }

        setContentView(R.layout.activity_identity);
        this.mTipsView = findViewById(R.id.tips);
        this.mTipsView.setText("请将人脸置于屏幕中央保持不动");

        this.mCameraPreviewView = findViewById(R.id.camera_preview);
        this.mCameraPreviewView.setStartListener(this);
        this.mSenseCamera = new SenseCamera.Builder(this).setFacing(SenseCamera.CAMERA_FACING_FRONT)
                .setRequestedPreviewSize(640, 480)
                .build();
        this.overlay=findViewById(R.id.overlay_silent);

        final File externalAssets = new File(this.getFilesDir(), "assets");

        FileUtil.copyAssetsToFile(this, LICENSE_FILE_NAME,
                new File(externalAssets, LICENSE_FILE_NAME).getAbsolutePath());
        FileUtil.copyAssetsToFile(this, DETECTION_MODEL_FILE_NAME,
                new File(externalAssets, DETECTION_MODEL_FILE_NAME).getAbsolutePath());
        FileUtil.copyAssetsToFile(this, ALIGNMENT_MODEL_FILE_NAME,
                new File(externalAssets, ALIGNMENT_MODEL_FILE_NAME).getAbsolutePath());
        FileUtil.copyAssetsToFile(this, QUALITY_MODEL_FILE_NAME,
                new File(externalAssets, QUALITY_MODEL_FILE_NAME).getAbsolutePath());
        FileUtil.copyAssetsToFile(this, FRAME_SELECTOR_MODEL_FILE_NAME,
                new File(externalAssets, FRAME_SELECTOR_MODEL_FILE_NAME).getAbsolutePath());
        FileUtil.copyAssetsToFile(this, ANTI_SPOOFING_MODEL_FILE_NAME,
                new File(externalAssets, ANTI_SPOOFING_MODEL_FILE_NAME).getAbsolutePath());
    }
    @Override
    protected void onResume() {
        super.onResume();

        try {
            this.mCameraPreviewView.start(this.mSenseCamera);
            this.mSenseCamera.setOnPreviewFrameCallback(this);
        } catch (Exception e) {
            setResult(ActivityUtils.RESULT_CODE_CAMERA_ERROR);
            this.finish();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        this.mStartInputData = false;
        SilentLivenessApi.release();

        this.mCameraPreviewView.stop();
        this.mCameraPreviewView.release();

        if (this.mIsCanceled) {
            this.mIsCanceled = false;
            setResult(0x101);
        }

        if (!isFinishing()) {
            finish();
        }
    }

    @Override
    public void onFail() {
        final Intent data = new Intent();
        data.putExtra(RESULT_DEAL_ERROR_INNER, true);
        setResult(ActivityUtils.RESULT_CODE_CAMERA_ERROR, data);
        if (!isFinishing()) {
            this.finish();
        }
    }

    protected boolean checkPermission(String... permissions) {
        if (permissions == null || permissions.length < 1) {
            return true;
        }
        if (Build.VERSION.SDK_INT < 23) {
            return true;
        }
        for (String permission : permissions) {
            if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }

    /**
     * Show error message.
     * @param errorMessage 信息内容
     */
    protected void showError(final String errorMessage) {
        runOnUiThread(() -> {
            if (!TextUtils.isEmpty(errorMessage)) {
                Toast.makeText(IdentityActivity.this,errorMessage,Toast.LENGTH_SHORT).show();
            }
        });
    }

    protected String getErrorNotice(ResultCode resultCode) {
        switch (resultCode) {
            case STID_E_CALL_API_IN_WRONG_STATE:
                return "错误的方法状态调用";
            case STID_E_CAPABILITY_NOT_SUPPORTED:
                return "授权文件能力不支持";
            case STID_E_LICENSE_PLATFORM_NOT_SUPPORTED:
                return "License不支持当前平台";
            case STID_E_LICENSE_INVALID:
                return "未通过授权验证";
            case STID_E_MODEL_INVALID:
                return "模型文件错误";
            case STID_E_MODEL_EXPIRE:
                return "模型文件过期";
            case STID_E_LICENSE_FILE_NOT_FOUND:
                return "授权文件不存在";
            case STID_E_LICENSE_BUNDLE_ID_INVALID:
                return "绑定包名错误";
            case STID_E_LICENSE_EXPIRE:
                return "授权文件过期";
            case STID_E_MODEL_FILE_NOT_FOUND:
                return "模型文件不存在";
            case STID_E_API_KEY_SECRET_NULL:
            case STID_E_API_KEY_INVALID:
                return "API_KEY 或 API_SECRET 错误";
            case STID_E_TIMEOUT:
                return "检测超时";
            case STID_E_SERVER_ACCESS:
                return "服务器访问错误";
            case STID_E_CHECK_CONFIG_FAIL:
                return "启动配置错误";
            case STID_E_NOFACE_DETECTED:
                return "动作幅度过大,请保持人脸在屏幕中央,重试一次";
            case STID_E_DETECT_FAIL:
            case STID_E_HACK:
                return "活体检测失败";
            case STID_E_SERVER_TIMEOUT:
                return "网络请求超时";
            case STID_E_FACE_COVERED:
                return "请调整人脸姿态，去除面部遮挡，正对屏幕重试一次";
            case STID_E_INVALID_ARGUMENTS:
                return "参数设置不合法";
            case STID_E_DETECTION_MODEL_FILE_NOT_FOUND:
                return "DETECTION模型文件不存在";
            case STID_E_ALIGNMENT_MODEL_FILE_NOT_FOUND:
                return "ALIGNMENT模型文件不存在";
            case STID_E_FRAME_SELECTOR_MODEL_FILE_NOT_FOUND:
                return "FRAME_SELECTOR模型文件不存在";
            case STID_E_ANTI_SPOOFING_MODEL_FILE_NOT_FOUND:
                return "ANTI_SPOOFING模型文件不存在";

            default:
                return null;
        }
    }
    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        final File externalAssets = new File(this.getFilesDir(), "assets");
        SilentLivenessApi.init(IdentityActivity.this,
                new File(externalAssets, LICENSE_FILE_NAME).getAbsolutePath(),
                new File(externalAssets, DETECTION_MODEL_FILE_NAME).getAbsolutePath(),
                new File(externalAssets, ALIGNMENT_MODEL_FILE_NAME).getAbsolutePath(),
                new File(externalAssets, QUALITY_MODEL_FILE_NAME).getAbsolutePath(),
                new File(externalAssets, FRAME_SELECTOR_MODEL_FILE_NAME).getAbsolutePath(),
                new File(externalAssets, ANTI_SPOOFING_MODEL_FILE_NAME).getAbsolutePath(),
                new OnLivenessListener() {
                    private long lastStatusUpdateTime;
                    @Override
                    public void onInitialized() {
                        SilentLivenessApi.start();
                        mStartInputData = true;
                        SilentLivenessApi.setBlurryFilterEnable(true, 1.4F);
                        SilentLivenessApi.setIlluminationFilterEnable(true, 1.899F, 4.997F);
                    }
                    @Override
                    public void onFaceStatusChanged(final int facePosition, final FaceOcclusion faceOcclusion, final int faceDistance, final int lightIntensity) {
                        if (SystemClock.elapsedRealtime() - this.lastStatusUpdateTime < 300 && facePosition != FacePosition.NORMAL) {
                            return;
                        }
                        if (facePosition == FacePosition.NO_FACE) {
                            mTipsView.setText("请将人脸置于屏幕中央保持不动");
                        } else if (faceDistance == FaceDistance.TOO_CLOSE) {
                            mTipsView.setText("请移动手机远离面部");
                        } else if (facePosition == FacePosition.OUT_OF_BOUND) {
                            mTipsView.setText("请将人脸置于屏幕中央保持不动");
                        } else if (null != faceOcclusion && faceOcclusion.isOcclusion()) {
                            final StringBuilder builder = new StringBuilder();
                            boolean needComma = false;
                            if (faceOcclusion.getBrowOcclusionStatus() == OcclusionStatus.OCCLUSION) {
                                builder.append("眉毛");
                                needComma = true;
                            }
                            if (faceOcclusion.getEyeOcclusionStatus() == OcclusionStatus.OCCLUSION) {
                                builder.append(needComma ? "、" : "");
                                builder.append("眼部");
                                needComma = true;
                            }
                            if (faceOcclusion.getNoseOcclusionStatus() == OcclusionStatus.OCCLUSION) {
                                builder.append(needComma ? "、" : "");
                                builder.append("鼻部");
                                needComma = true;
                            }
                            if (faceOcclusion.getMouthOcclusionStatus() == OcclusionStatus.OCCLUSION) {
                                builder.append(needComma ? "、" : "");
                                builder.append("嘴部");
                            }

                            mTipsView.setText("请正对手机");
                        } else if (lightIntensity == LightIntensity.TOO_DARK) {
                            IdentityActivity.this.mTipsView.setText("当前光线过暗，请调整光线");
                        } else if (lightIntensity == LightIntensity.TOO_BRIGHT) {
                            IdentityActivity.this.mTipsView.setText("当前光线过亮，请调整光线");
                        } else if (faceDistance == FaceDistance.TOO_FAR) {
                            mTipsView.setText("请移动手机靠近面部");
                        } else {
                            mTipsView.setText("活体检测中");
                        }
                        this.lastStatusUpdateTime = SystemClock.elapsedRealtime();
                    }

                    @Override
                    public void onFailure(ResultCode resultCode, byte[] protobufData, List imageData, List<Rect> faceRectList) {
                        IdentityActivity.this.mStartInputData = false;
                        IdentityActivity.this.mIsCanceled = false;
                        final Intent data = new Intent();
                        data.putExtra(RESULT_SDK_ERROR_CODE, resultCode.name());
                        switch (resultCode) {
                            case STID_E_TIMEOUT:
                            case STID_E_HACK:
                            case STID_E_DETECT_FAIL:
                                data.putExtra(RESULT_INFO, getErrorNotice(resultCode));
                                data.putExtra(RESULT_DEAL_ERROR_INNER, false);
                                setResult(ActivityUtils.convertResultCode(resultCode), data);
                                break;
                            /*End*/
                            default:
                                data.putExtra(RESULT_DEAL_ERROR_INNER, true);
                                IdentityActivity.this.showError(getErrorNotice(resultCode));
                                setResult(ActivityUtils.convertResultCode(resultCode), data);
                                break;
                        }
                        finish();
                    }

                    @Override
                    public void onSuccess(byte[] protobufData, List imageData, List<Rect> faceRects) {
                        IdentityActivity.this.mStartInputData = false;
                        IdentityActivity.this.mIsCanceled = false;

                        final Intent data = new Intent();
                        data.putExtra(RESULT_DEAL_ERROR_INNER, false);

                        List<byte[]> imageResult = (List<byte[]>) imageData;
                        if (imageResult != null && !imageResult.isEmpty()) {
                            Bitmap bitmap = BitmapFactory.decodeByteArray(imageResult.get(0), 0, imageResult.get(0).length);
                            String imageKey = "silent_image_" + System.currentTimeMillis();
                            SimpleImageStore.getInstance().put(imageKey, bitmap);
                            data.putExtra(RESULT_IMAGE_KEY, imageKey);
                            data.putExtra(RESULT_FACE_RECT, faceRects.get(0));
                        }

                        setResult(RESULT_OK, data);
                        finish();
                    }
                });
        SilentLivenessApi.setFaceDistanceRate(0.4F, 0.8F);
    }
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        if (!this.mStartInputData) {
            return;
        }
        final int width = this.mSenseCamera.getPreviewSize().getWidth();
        final int height = this.mSenseCamera.getPreviewSize().getHeight();
        SilentLivenessApi.inputData(data, PixelFormat.NV21, new Size(width, height), this.mCameraPreviewView.convertViewRectToPicture(this.overlay.getMaskBounds()), true, mSenseCamera.getRotationDegrees());
    }
}
